Skip to content

fix(call): restore Bluetooth audio routing - #6567

Open
flake92 wants to merge 3 commits into
nextcloud:masterfrom
flake92:fix/bluetooth-call-routing
Open

fix(call): restore Bluetooth audio routing#6567
flake92 wants to merge 3 commits into
nextcloud:masterfrom
flake92:fix/bluetooth-call-routing

Conversation

@flake92

@flake92 flake92 commented Aug 24, 2026

Copy link
Copy Markdown

Prefer call-capable Bluetooth communication devices and preserve selection across reconnects.

Assisted-by: Codex:gpt-5

🖼️ Screenshots

🏚️ Before 🏡 After
B A

🚧 TODO

  • ...

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔖 Capability is checked or not needed
  • 🔙 Backport requests are created or not needed: /backport to stable-xx.x
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

@flake92 flake92 closed this Aug 24, 2026
@flake92
flake92 force-pushed the fix/bluetooth-call-routing branch from 2a60dbe to 97bb606 Compare August 24, 2026 23:44
@flake92
flake92 deleted the fix/bluetooth-call-routing branch August 24, 2026 23:44
@flake92 flake92 reopened this Aug 24, 2026
@mahibi

mahibi commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Thank you for contributing @flake92 🙏

It would be great if the new java class could be converted to kotlin.
Feel free to also have a look at the review from Claude AI.

Just let us know if you want to do this (that would be awesome!) or if we should take over.


Claude AI review:

Correctness (4 findings)

  • No audio route for ~4s on a failed device switch (WebRtcAudioManager.java:366): if the user taps Earpiece while on Bluetooth and the switch transiently fails, the rollback-to-Bluetooth logic can't actually restart Bluetooth (it's mid-teardown), leaving the call with silence until the async disconnect settles.
  • Audio-focus flapping bypasses the retry cap (WebRtcBluetoothManager.java:255): reassertBluetoothAudioAfterFocusGain() resets the SCO attempt counter on every focus-gain event, so MAX_SCO_CONNECTION_ATTEMPTS isn't actually a bound over a call's lifetime if focus flaps repeatedly (e.g. notification sounds).
  • Stale getter can resurrect a route the user just left (WebRtcBluetoothManager.java:1174): a queued Bluetooth callback after an explicit clear can fall back to getCommunicationDevice(), which the code's own comment says is stale on some Samsung devices — risk of Bluetooth "coming back" after the user switched away.
  • Wired-headset detection is inconsistent between two gates (WebRtcAudioManager.java:513): matchesAudioDevice() recognizes more USB/wired types than hasWiredHeadset() does, so the newer, broader matching is currently dead code — a latent trap if either is edited independently later.

Efficiency/simplification/reuse (6 findings) — lower severity: exhausted retries keep re-triggering startScoAudio() harmlessly forever; the legacy vs. Android-12+ paths are interleaved via SDK-version branches across ~11 call sites instead of being split behind an interface (real maintenance risk going forward); some duplicated device-enumeration logic between WebRtcAudioManager and ModernBluetoothRoute; one dead code branch; one needless one-line wrapper.

The two "removed behavior" concerns I'd have flagged up front — un-stickying Bluetooth selection on disconnect, and a possible SCO_DISCONNECTING race in requestBluetoothAudioSelection() — were investigated and ruled out: the first is the intentional point of the fix, the second self-heals within the 4s timeout.

Given this is call-audio-routing code (hard to unit test end-to-end, easy to regress silently), I'd suggest at minimum fixing the first two correctness findings before merging, and manually testing the "switch away from Bluetooth mid-call" and "notification during a Bluetooth call" paths on a real device.

@flake92

flake92 commented Aug 26, 2026

Copy link
Copy Markdown
Author

@mahibi

Changes:

  • Converted the new production policy class and all newly added tests from Java to Kotlin.
  • Preserved the current working audio route when setCommunicationDevice() fails.
  • Confirmed a new non-Bluetooth route before ending Bluetooth routing state.
  • Preserved the Bluetooth retry counter across audio-focus loss and recovery.
  • Enforced retry limits for Bluetooth auto-start and route reassertion.
  • Prevented further automatic retries after the retry budget is exhausted.
  • Ignored stale communication-device getters and callbacks.
  • Rejected delayed Bluetooth callbacks after the user selected a non-Bluetooth output.
  • Unified wired headset, headphone, and USB-device detection.
  • Added tests for failed route changes, focus recovery, bounded retries, stale callbacks, late Bluetooth activation, wired-device detection, and exhausted retries.
  • Added no new .java files relative to the PR base.
  • Passed ktlint, detekt, targeted WebRTC tests, and the complete GPlay debug unit-test suite.
    Pending manual hardware checks:
  • Switching from Bluetooth to another output during an active call.
  • Receiving a notification during a Bluetooth call and confirming correct route recovery.

flake92 and others added 3 commits August 27, 2026 00:20
Prefer call-capable Bluetooth communication devices and preserve selection across reconnects.

Assisted-by: Codex:gpt-5
Signed-off-by: Oleg Cherry <80347136+flake92@users.noreply.github.com>
Assisted-by: Codex:gpt-5
Signed-off-by: Oleg Cherry <80347136+flake92@users.noreply.github.com>
Keep the working route when a device switch is rejected, preserve bounded retry state across focus changes, reject stale callbacks, and share wired-device detection. Convert the new policy and tests to Kotlin.

Assisted-by: Codex:gpt-5
Signed-off-by: flake92 <flake92@live.com>
@flake92
flake92 force-pushed the fix/bluetooth-call-routing branch from 98c2aba to ef84967 Compare August 26, 2026 21:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review Waiting for reviews AI assisted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants